package corejava.corejava1.ch14;

import java.io.*;
import java.util.*;
import java.util.concurrent.*;

/**
 * @version 1.0 2004-08-01
 * @author Cay Horstmann
 */
public class Ch14_07_BlockingQueueTest {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        System.out.print("Enter base directory (e.g. /usr/local/jdk1.6.0/src): ");
        String directory = in.nextLine();
        System.out.print("Enter keyword (e.g. volatile): ");
        String keyword = in.nextLine();

        final int FILE_QUEUE_SIZE = 10;
        final int SEARCH_THREADS = 100;

        // 很多情况下，不需要在代码实现显式线程同步的细节，可以直接使用线程安全的队列（自己不需要写显示的同步代码）
        BlockingQueue<File> queue = new ArrayBlockingQueue<File>(FILE_QUEUE_SIZE);

        FileEnumerationTask enumerator = new FileEnumerationTask(queue, new File(directory));
        new Thread(enumerator).start();
        for (int i = 1; i <= SEARCH_THREADS; i++)
            new Thread(new SearchTask(queue, keyword)).start();
    }
}

/**
 * This task enumerates all files in a directory and its subdirectories.
 * 程序在一个目录及它的所有子目录下搜索所有文件
 */
class FileEnumerationTask implements Runnable {
    /**
     * Constructs a FileEnumerationTask.
     * 
     * @param queue
     *            the blocking queue to which the enumerated files are added
     * @param startingDirectory
     *            the directory in which to start the enumeration
     */
    public FileEnumerationTask(BlockingQueue<File> queue, File startingDirectory) {
        this.queue = queue;
        this.startingDirectory = startingDirectory;
    }

    public void run() {
        try {
            enumerate(startingDirectory);
            queue.put(DUMMY);               // 定义一个File变量DUMMY用来标记结束
        } catch (InterruptedException e) {
        }
    }

    /**
     * Recursively enumerates all files in a given directory and its
     * subdirectories
     * 
     * @param directory
     *            the directory in which to start
     */
    public void enumerate(File directory) throws InterruptedException {
        File[] files = directory.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                enumerate(file);    // 递归查找
            } else {
                queue.put(file);    // 添加一个元素，如果队列满，则阻塞(通常和take方法配合使用)
            }
        }
    }

    public static File          DUMMY = new File("");

    private BlockingQueue<File> queue;
    private File                startingDirectory;
}

/**
 * This task searches files for a given keyword.
 */
class SearchTask implements Runnable {
    /**
     * Constructs a SearchTask.
     * 
     * @param queue
     *            the queue from which to take files
     * @param keyword
     *            the keyword to look for
     */
    public SearchTask(BlockingQueue<File> queue, String keyword) {
        this.queue = queue;
        this.keyword = keyword;
    }

    public void run() {
        try {
            boolean done = false;
            while (!done) {
                File file = queue.take();   // 移出并返回头元素，如果线程为空，取元素的操作会阻塞
                if (file == FileEnumerationTask.DUMMY) {
                    queue.put(file);
                    done = true;
                } else
                    search(file);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
        }
    }

    /**
     * Searches a file for a given keyword and prints all matching lines.
     * 
     * @param file
     *            the file to search
     */
    public void search(File file) throws IOException {
        Scanner in = new Scanner(new FileInputStream(file));
        int lineNumber = 0;
        while (in.hasNextLine()) {
            lineNumber++;
            String line = in.nextLine();
            if (line.contains(keyword))
                System.out.printf("%s:%d:%s%n", file.getPath(), lineNumber, line);
        }
        in.close();
    }

    private BlockingQueue<File> queue;
    private String              keyword;
}
